/**
 * This file is Copyright © 2008 Software Craftsmen Limited. All Rights Reserved.
 */
package com.softwarecraftsmen.orogeny.filing;

import com.softwarecraftsmen.orogeny.filing.findFileFilters.FindFilesFilter;
import com.softwarecraftsmen.orogeny.filing.findSubdirectoriesFilters.FindSubdirectoriesFilter;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.StringWriter;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

public class AbsoluteFiles implements AbsolutePaths, Iterable<AbsoluteFile>
{
	@NotNull
	public static final AbsoluteFiles EmptyAbsoluteFiles = new AbsoluteFiles(new LinkedHashSet<AbsoluteFile>());

	private final Set<AbsoluteFile> files;
	private static final Set<AbsoluteDirectory> EmptyDirectories = Collections.emptySet();

	public AbsoluteFiles(final @NotNull Set<AbsoluteFile> files)
	{
		this.files = files;
	}

	@NotNull
	public AbsolutePaths and(final @NotNull AbsolutePath absolutePath)
	{
		if (absolutePath instanceof AbsoluteFile)
		{
			return and((AbsoluteFile)absolutePath);
		}
		return new AbsoluteDirectoriesAndFiles(new LinkedHashSet<AbsolutePath>(files)
		{{
			add(absolutePath);
		}});
	}

	@NotNull
	public AbsoluteDirectoriesAndFiles and(final @NotNull AbsoluteDirectory absoluteDirectory)
	{
		return new AbsoluteDirectoriesAndFiles(new LinkedHashSet<AbsolutePath>(files)
		{{
			add(absoluteDirectory);
		}});
	}

	@NotNull
	public AbsoluteFiles and(final @NotNull AbsoluteFile absoluteFile)
	{
		return new AbsoluteFiles(new LinkedHashSet<AbsoluteFile>(files)
		{{
			add(absoluteFile);
		}});
	}

	@NotNull
	public AbsoluteDirectoriesAndFiles and(final @NotNull AbsolutePaths absolutePaths)
	{
		return new AbsoluteDirectoriesAndFiles(new LinkedHashSet<AbsolutePath>(files)
		{{
			addAll(absolutePaths.directories());
			addAll(absolutePaths.files());
		}});
	}

	@NotNull
	public AbsoluteDirectoriesAndFiles and(final @NotNull AbsoluteDirectories absoluteDirectories)
	{
		return new AbsoluteDirectoriesAndFiles(new LinkedHashSet<AbsolutePath>(files)
		{{
			addAll(absoluteDirectories.directories());
		}});
	}

	@NotNull
	public AbsoluteFiles and(final @NotNull AbsoluteFiles absoluteFiles)
	{
		return new AbsoluteFiles(new LinkedHashSet<AbsoluteFile>(files)
		{{
			addAll(absoluteFiles.files);
		}});
	}

	@NotNull
	public AbsoluteDirectoriesAndFiles and(final @NotNull AbsoluteDirectoriesAndFiles absoluteDirectoriesAndFiles)
	{
		return new AbsoluteDirectoriesAndFiles(new LinkedHashSet<AbsolutePath>(files)
		{{
			addAll(absoluteDirectoriesAndFiles.directories());
			addAll(absoluteDirectoriesAndFiles.files());
		}});
	}

	@SuppressWarnings({"ManualArrayToCollectionCopy"})
	@NotNull
	public static AbsoluteFiles absoluteFiles(final @NotNull AbsoluteFile... absoluteFiles)
	{
		return new AbsoluteFiles(new LinkedHashSet<AbsoluteFile>()
		{
			{
				for (AbsoluteFile absoluteFile : absoluteFiles)
				{
					add(absoluteFile);
				}
			}
		});
	}

	@NotNull
	public static AbsoluteFiles absoluteFiles(final @NotNull Set<AbsoluteFile> files)
	{
		return new AbsoluteFiles(files);
	}

	@NotNull
	public static AbsoluteFiles absoluteFiles(final @NotNull AbsolutePaths absolutePaths, final @NotNull FindFilesFilter findFilesFilter)
	{
		return new AbsoluteFiles(absolutePaths.findFiles(findFilesFilter));
	}

	@SuppressWarnings({"ManualArrayToCollectionCopy"})
	@NotNull
	public static AbsoluteFiles absoluteFiles(final @NotNull AbsoluteFiles... absoluteFiles)
	{
		final AbsoluteFiles actual;
		if (absoluteFiles.length == 0)
		{
			actual = EmptyAbsoluteFiles;
		}
		else
		{
			AbsoluteFiles nearActual = absoluteFiles[0];
			for (int index = 1; index < absoluteFiles.length; index++)
			{
				final AbsoluteFiles fromAbsoluteFiles = absoluteFiles[index];
				nearActual = nearActual.and(fromAbsoluteFiles);
			}
			actual = nearActual;
		}
		return actual;
	}

	@NotNull
	public String toFileSystemSpecificPath()
	{
		final StringWriter writer = new StringWriter();
		boolean afterFirst = false;
		for (AbsoluteFile absoluteFile : files)
		{
			if (afterFirst)
			{
				writer.write(absoluteFile.toFileSystemSpecificPathPrefixedByPathSeparator());
			}
			else
			{
				writer.write(absoluteFile.toFileSystemSpecificPath());
			}
			afterFirst = true;
		}
		return writer.toString();
	}

	public boolean exists()
	{
		return false;
	}

	@NotNull
	public Set<File> toFiles()
	{
		return new LinkedHashSet<File>()
		{{
			for (AbsolutePath absoluteFile : files)
			{
				add(absoluteFile.toFile());
			}
		}};
	}

	@NotNull
	public Set<AbsoluteFile> findFiles(final @NotNull FindFilesFilter findFilesFilter)
	{
		return new LinkedHashSet<AbsoluteFile>()
		{{
			for (AbsoluteFile absoluteFile : files)
			{
				addAll(absoluteFile.findFiles(findFilesFilter));
			}
		}};
	}

	@NotNull
	public Set<AbsoluteDirectory> findSubdirectories(final @NotNull FindSubdirectoriesFilter findSubdirectoriesFilter)
	{
		return EmptyDirectories;
	}

	@NotNull
	public Set<AbsoluteFile> files()
	{
		return new LinkedHashSet<>(files);
	}

	@NotNull
	public Set<AbsoluteDirectory> directories()
	{
		return EmptyDirectories;
	}

	public boolean isEmpty()
	{
		return files.isEmpty();
	}

	@NotNull
	public Iterator<AbsoluteFile> iterator()
	{
		return files.iterator();
	}
}
